use super::*;
use crate::{
    Land,
    all::ForestKind,
    site::util::{Dir, sprites::PainterSpriteExt},
    util::{DIRS, RandomField, Sampler},
};
use common::{
    calendar::{Calendar, CalendarEvent},
    terrain::{Block, BlockKind, SpriteKind},
};
use rand::{prelude::*, seq::IndexedRandom};
use strum::IntoEnumIterator;
use vek::*;

#[derive(Copy, Clone)]
enum Style {
    Wooden(Rgb<u8>),
    Stone,
    Brick,
    Daub,
}

impl Style {
    fn get_fill(self) -> Fill {
        match self {
            Self::Wooden(color) => Fill::PlankWall(BlockKind::Wood, color, 64),
            Self::Stone => Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 50),
            Self::Daub => Fill::Brick(BlockKind::Wood, Rgb::new(200, 180, 150), 24),
            Self::Brick => Fill::Brick(BlockKind::Rock, Rgb::new(100, 32, 20), 48),
        }
    }
}

/// Represents house data generated by the `generate()` method
pub struct House {
    /// Tile position of the door tile
    pub door_tile: Vec2<i32>,
    /// Axis aligned bounding region of tiles
    tile_aabr: Aabr<i32>,
    /// Axis aligned bounding region for the house
    bounds: Aabr<i32>,
    /// Approximate altitude of the door tile
    pub(crate) alt: i32,
    /// Number of floors
    levels: u32,
    /// Difference between a level and the floor above
    overhang: i32,
    /// Color of the roof
    roof_color: Rgb<u8>,
    front: Dir,
    christmas_decorations: bool,
    lower_style: Style,
    upper_style: Style,
    gable_style: Style,
}

impl House {
    pub fn generate(
        land: &Land,
        rng: &mut impl Rng,
        site: &Site,
        door_tile: Vec2<i32>,
        door_dir: Vec2<i32>,
        tile_aabr: Aabr<i32>,
        calendar: Option<&Calendar>,
        alt: Option<i32>,
    ) -> Self {
        let levels = rng.random_range(1..2 + (tile_aabr.max - tile_aabr.min).product() / 6) as u32;
        let bounds = Aabr {
            min: site.tile_wpos(tile_aabr.min),
            max: site.tile_wpos(tile_aabr.max),
        };

        let front = match door_dir {
            dir if dir.y < 0 => Dir::NegY,
            dir if dir.x < 0 => Dir::NegX,
            dir if dir.y > 0 => Dir::Y,
            _ => Dir::X,
        };

        let christmas_decorations = calendar.is_some_and(|c| c.is_event(CalendarEvent::Christmas));

        let door_wpos = site.tile_center_wpos(door_tile);
        let upper_style = match land.make_forest_lottery(door_wpos).choose_seeded(rng.random())
            // Don't use wood for some houses, even in woody areas
            .filter(|_| rng.random_bool(0.75))
        {
            Some(
                ForestKind::Cedar
                | ForestKind::AutumnTree
                | ForestKind::Frostpine
                | ForestKind::Mangrove,
            ) => Style::Wooden(Rgb::new(63, 28, 12)),
            Some(ForestKind::Oak | ForestKind::Swamp | ForestKind::Baobab) => {
                Style::Wooden(Rgb::new(102, 87, 63))
            },
            Some(ForestKind::Acacia | ForestKind::Birch | ForestKind::Palm) => {
                Style::Wooden(Rgb::new(130, 104, 102))
            },
            Some(
                ForestKind::Mapletree | ForestKind::Redwood | ForestKind::Pine | ForestKind::Cherry,
            ) => Style::Wooden(Rgb::new(117, 95, 46)),
            _ if rng.random_bool(0.5) => Style::Brick,
            _ => Style::Daub,
        };
        let lower_style = match upper_style {
            // Nothing else can support stone
            Style::Stone => Style::Stone,
            Style::Brick => Style::Brick,
            // Stone may appear below anything else
            _ if rng.random_bool(0.3) => Style::Brick,
            _ if rng.random_bool(0.5) => Style::Stone,
            _ => upper_style,
        };
        let gable_style = match upper_style {
            Style::Wooden(_) | Style::Daub => upper_style,
            Style::Stone | Style::Brick => Style::Daub,
        };

        Self {
            door_tile,
            tile_aabr,
            bounds,
            alt: alt.unwrap_or_else(|| {
                land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
            }),
            levels,
            overhang: if levels > 3 {
                // Overhangs of 3 at this building height are ill-advised.
                // Failure to comply with Veloren building code will result
                // in a fine and a revoked building permit.
                *[-5, 1, 2].choose(rng).unwrap_or(&-5)
            } else if levels > 1 {
                *[-5, 1, 2, 3].choose(rng).unwrap_or(&2)
            } else {
                // Single story buildings require no overhangs
                0
            },
            roof_color: {
                let colors = [
                    Rgb::new(21, 43, 48),
                    Rgb::new(11, 23, 38),
                    Rgb::new(45, 28, 21),
                    Rgb::new(10, 55, 40),
                    Rgb::new(5, 35, 15),
                    Rgb::new(40, 5, 11),
                    Rgb::new(55, 45, 11),
                ];
                *colors.choose(rng).unwrap_or(&Rgb::new(21, 43, 48))
            },
            front,
            christmas_decorations,
            lower_style,
            upper_style,
            gable_style,
        }
    }

    pub fn z_range(&self) -> Range<i32> { self.alt..self.alt + self.levels as i32 * STOREY }

    pub fn roof_color(&self) -> Rgb<u8> { self.roof_color }
}

const STOREY: i32 = 5;

impl Structure for House {
    #[cfg(feature = "use-dyn-lib")]
    const UPDATE_FN: &'static [u8] = b"render_house\0";

    #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_house"))]
    fn render_inner(&self, site: &Site, _land: &Land, painter: &Painter) {
        let storey = STOREY;
        let roof = storey * self.levels as i32 - 1;
        let foundations = 20;
        let alt = self.alt + 1;
        let door_tile_wpos = site.tile_center_wpos(self.door_tile);

        let wall_fill_upper = self.upper_style.get_fill();
        let wall_fill_lower = self.lower_style.get_fill();
        let wall_fill_gable = self.gable_style.get_fill();

        let pillar_fill = match self.upper_style {
            Style::Wooden(color) => Fill::Block(Block::new(BlockKind::Wood, color / 2)),
            Style::Brick => Fill::Block(Block::new(BlockKind::Rock, Rgb::new(85, 35, 8))),
            // Style::Brick => Fill::Block(Block::new(BlockKind::Rock, Rgb::new(40, 44, 45))),
            _ => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))),
        };

        // Roof
        let roof_lip = 1;
        let roof_height = (self.bounds.min - self.bounds.max)
            .map(|e| e.abs())
            .reduce_min()
            / 2
            + roof_lip
            + 1;

        let (roof_primitive, roof_empty) = match self.front {
            Dir::Y => {
                (
                    painter.prim(Primitive::Gable {
                        aabb: Aabb {
                            min: (self.bounds.min - roof_lip).with_z(alt + roof),
                            max: (Vec2::new(
                                self.bounds.max.x + 1 + roof_lip,
                                self.bounds.max.y
                                    + 1
                                    + roof_lip
                                    + (self.levels as i32 - 1) * self.overhang,
                            ))
                            .with_z(alt + roof + roof_height),
                        },
                        inset: roof_height,
                        dir: Dir::Y,
                    }),
                    painter.prim(Primitive::Gable {
                        aabb: Aabb {
                            min: (Vec2::new(self.bounds.min.x, self.bounds.min.y - 1))
                                .with_z(alt + roof), /* self.bounds.min - roof_lip).with_z(alt +
                                                      * roof), */
                            max: (Vec2::new(
                                self.bounds.max.x + 1,
                                self.bounds.max.y
                                    + 1
                                    + (self.levels as i32 - 1) * self.overhang
                                    + 1,
                            ))
                            .with_z(alt + roof + roof_height - 1),
                        },
                        inset: roof_height - 1,
                        dir: Dir::Y,
                    }),
                )
            },
            Dir::X => {
                (
                    painter.prim(Primitive::Gable {
                        aabb: Aabb {
                            min: (self.bounds.min - roof_lip).with_z(alt + roof),
                            max: Vec2::new(
                                self.bounds.max.x
                                    + 1
                                    + roof_lip
                                    + (self.levels as i32 - 1) * self.overhang,
                                self.bounds.max.y + 1 + roof_lip,
                            )
                            .with_z(alt + roof + roof_height),
                        },
                        inset: roof_height,
                        dir: Dir::X,
                    }),
                    painter.prim(Primitive::Gable {
                        aabb: Aabb {
                            min: (Vec2::new(self.bounds.min.x - 1, self.bounds.min.y))
                                .with_z(alt + roof), /* self.bounds.min - roof_lip).with_z(alt +
                                                      * roof), */
                            max: Vec2::new(
                                self.bounds.max.x
                                    + 1
                                    + (self.levels as i32 - 1) * self.overhang
                                    + 1,
                                self.bounds.max.y + 1,
                            )
                            .with_z(alt + roof + roof_height - 1),
                        },
                        inset: roof_height - 1,
                        dir: Dir::X,
                    }),
                )
            },
            Dir::NegY => (
                painter.prim(Primitive::Gable {
                    aabb: Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - roof_lip,
                            self.bounds.min.y - roof_lip - (self.levels as i32 - 1) * self.overhang,
                        )
                        .with_z(alt + roof),
                        max: Vec2::new(
                            self.bounds.max.x + 1 + roof_lip,
                            self.bounds.max.y + roof_lip + 1,
                        )
                        .with_z(alt + roof + roof_height),
                    },
                    inset: roof_height,
                    dir: Dir::Y,
                }),
                painter.prim(Primitive::Gable {
                    aabb: Aabb {
                        min: Vec2::new(
                            self.bounds.min.x,
                            self.bounds.min.y - 1 - (self.levels as i32 - 1) * self.overhang - 1,
                        )
                        .with_z(alt + roof),
                        max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + roof_lip + 1)
                            .with_z(alt + roof + roof_height - 1),
                    },
                    inset: roof_height - 1,
                    dir: Dir::Y,
                }),
            ),
            _ => (
                painter.prim(Primitive::Gable {
                    aabb: Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - roof_lip - (self.levels as i32 - 1) * self.overhang,
                            self.bounds.min.y - roof_lip,
                        )
                        .with_z(alt + roof),
                        max: Vec2::new(
                            self.bounds.max.x + 1 + roof_lip,
                            self.bounds.max.y + 1 + roof_lip,
                        )
                        .with_z(alt + roof + roof_height),
                    },
                    inset: roof_height,
                    dir: Dir::X,
                }),
                painter.prim(Primitive::Gable {
                    aabb: Aabb {
                        min: Vec2::new(
                            self.bounds.min.x
                                - roof_lip
                                - 1
                                - (self.levels as i32 - 1) * self.overhang,
                            self.bounds.min.y,
                        )
                        .with_z(alt + roof),
                        max: Vec2::new(self.bounds.max.x + 1 + roof_lip, self.bounds.max.y + 1)
                            .with_z(alt + roof + roof_height - 1),
                    },
                    inset: roof_height - 1,
                    dir: Dir::X,
                }),
            ),
        };

        let (roof_front_wall, roof_rear_wall) = match self.front {
            Dir::Y => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: (Vec2::new(
                        self.bounds.min.x,
                        self.bounds.max.y + (self.levels as i32 - 1) * self.overhang,
                    ))
                    .with_z(alt + roof),
                    max: (Vec2::new(
                        self.bounds.max.x + 1,
                        self.bounds.max.y + (self.levels as i32 - 1) * self.overhang + 1,
                    ))
                    .with_z(alt + roof + roof_height),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: (Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof)),
                    max: (Vec2::new(self.bounds.max.x + 1, self.bounds.min.y + 1)
                        .with_z(alt + roof + roof_height)),
                })),
            ),
            Dir::X => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(
                        self.bounds.max.x + (self.levels as i32 - 1) * self.overhang,
                        self.bounds.min.y,
                    )
                    .with_z(alt + roof),
                    max: Vec2::new(
                        self.bounds.max.x + (self.levels as i32 - 1) * self.overhang + 1,
                        self.bounds.max.y + 1,
                    )
                    .with_z(alt + roof + roof_height),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + roof_height),
                })),
            ),
            Dir::NegY => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(
                        self.bounds.min.x,
                        self.bounds.min.y - (self.levels as i32 - 1) * self.overhang,
                    )
                    .with_z(alt + roof),
                    max: Vec2::new(
                        self.bounds.max.x + 1,
                        self.bounds.min.y - (self.levels as i32 - 1) * self.overhang + 1,
                    )
                    .with_z(alt + roof + roof_height),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.max.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + roof_height),
                })),
            ),
            _ => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(
                        self.bounds.min.x - (self.levels as i32 - 1) * self.overhang,
                        self.bounds.min.y,
                    )
                    .with_z(alt + roof),
                    max: Vec2::new(
                        self.bounds.min.x - (self.levels as i32 - 1) * self.overhang + 1,
                        self.bounds.max.y + 1,
                    )
                    .with_z(alt + roof + roof_height),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.max.x, self.bounds.min.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + roof_height),
                })),
            ),
        };
        let roof_front = painter.prim(Primitive::intersect(roof_empty, roof_front_wall));
        let roof_rear = painter.prim(Primitive::intersect(roof_empty, roof_rear_wall));
        painter.fill(
            roof_primitive,
            Fill::Brick(BlockKind::Wood, self.roof_color, 24),
        );
        painter.fill(roof_empty, Fill::Block(Block::empty()));
        let roof_walls = painter.prim(Primitive::union(roof_front, roof_rear));
        painter.fill(roof_walls, wall_fill_gable.clone());
        let max_overhang = (self.levels as i32 - 1) * self.overhang;
        let (roof_beam, roof_beam_right, roof_beam_left) = match self.front {
            Dir::Y => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1 + max_overhang)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y + 1 + max_overhang)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.max.x, self.bounds.min.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1 + max_overhang)
                        .with_z(alt + roof + 1),
                })),
            ),
            Dir::X => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + max_overhang + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + max_overhang + 1, self.bounds.min.y + 1)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.max.y).with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + max_overhang + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + 1),
                })),
            ),
            Dir::NegY => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y - max_overhang)
                        .with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.max.x, self.bounds.min.y - max_overhang)
                        .with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y - max_overhang)
                        .with_z(alt + roof),
                    max: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + 1),
                })),
            ),
            _ => (
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x - max_overhang - 1, self.bounds.min.y)
                        .with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x - max_overhang, self.bounds.min.y)
                        .with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.min.y + 1)
                        .with_z(alt + roof + 1),
                })),
                painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x - max_overhang, self.bounds.max.y)
                        .with_z(alt + roof),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + roof + 1),
                })),
            ),
        };
        let quarter_x = self.bounds.min.x + (self.bounds.max.x - self.bounds.min.x) / 4;
        let quarter_y = self.bounds.min.y + (self.bounds.max.y - self.bounds.min.y) / 4;
        let half_x = self.bounds.min.x + (self.bounds.max.x - self.bounds.min.x) / 2;
        let half_y = self.bounds.min.y + (self.bounds.max.y - self.bounds.min.y) / 2;
        let three_quarter_x = self.bounds.min.x + 3 * (self.bounds.max.x - self.bounds.min.x) / 4;
        let three_quarter_y = self.bounds.min.y + 3 * (self.bounds.max.y - self.bounds.min.y) / 4;
        let top_rafter = if self.front.is_y() {
            painter.prim(Primitive::Aabb(Aabb {
                min: (Vec2::new(half_x, self.bounds.min.y - 2 - max_overhang.abs())
                    .with_z(alt + roof)),
                max: (Vec2::new(half_x + 1, self.bounds.max.y + 2 + max_overhang.abs()))
                    .with_z(alt + roof + roof_height),
            }))
        } else {
            painter.prim(Primitive::Aabb(Aabb {
                min: (Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y)
                    .with_z(alt + roof)),
                max: (Vec2::new(self.bounds.max.x + 1 + max_overhang.abs(), half_y + 1))
                    .with_z(alt + roof + roof_height),
            }))
        };
        let left_rafter = if self.front.is_y() {
            painter.prim(Primitive::Plane(
                Aabr {
                    min: Vec2::new(half_x, self.bounds.min.y - 1 - max_overhang.abs()),
                    max: Vec2::new(
                        three_quarter_x + 1,
                        self.bounds.max.y + 1 + max_overhang.abs(),
                    ),
                },
                Vec2::new(half_x, self.bounds.min.y - 1 - max_overhang.abs()).with_z(alt + roof),
                Vec2::new(1.0, 0.0),
            ))
        } else {
            painter.prim(Primitive::Plane(
                Aabr {
                    min: Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y),
                    max: Vec2::new(
                        self.bounds.max.x + 1 + max_overhang.abs(),
                        three_quarter_y + 1,
                    ),
                },
                Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y).with_z(alt + roof),
                Vec2::new(0.0, 1.0),
            ))
        };
        let right_rafter = if self.front.is_y() {
            painter.prim(Primitive::Plane(
                Aabr {
                    min: Vec2::new(quarter_x, self.bounds.min.y - 1 - max_overhang.abs()),
                    max: Vec2::new(half_x + 1, self.bounds.max.y + 1 + max_overhang.abs()),
                },
                Vec2::new(half_x, self.bounds.min.y - 1 - max_overhang.abs()).with_z(alt + roof),
                Vec2::new(1.0, 0.0),
            ))
        } else {
            painter.prim(Primitive::Plane(
                Aabr {
                    min: Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), quarter_y),
                    max: Vec2::new(self.bounds.max.x + 1 + max_overhang.abs(), half_y + 1),
                },
                Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y).with_z(alt + roof),
                Vec2::new(0.0, 1.0),
            ))
        };
        let rafters1 = painter.prim(Primitive::union(left_rafter, right_rafter));
        let rafters2 = painter.prim(Primitive::union(rafters1, top_rafter));

        painter.fill(
            painter.prim(Primitive::intersect(roof_beam, roof_walls)),
            pillar_fill.clone(),
        );
        painter.fill(
            painter.prim(Primitive::union(roof_beam_left, roof_beam_right)),
            pillar_fill.clone(),
        );
        painter.fill(
            painter.prim(Primitive::intersect(rafters2, roof_walls)),
            pillar_fill.clone(),
        );

        // Walls
        // For each storey...
        for i in 1..self.levels + 1 {
            let previous_height = (storey * (i as i32 - 1) - 1).max(-1);
            let height = storey * i as i32 - 1;
            let window_height = storey - 3;
            let storey_increase = (i as i32 - 1) * self.overhang;

            // Walls
            let inner_level = if self.overhang < -4 && i > 1 {
                match self.front {
                    Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                        min: (self.bounds.min + 1).with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x, self.bounds.max.y + storey_increase + 1)
                            .with_z(alt + height),
                    })),
                    Dir::X => painter.prim(Primitive::Aabb(Aabb {
                        min: (self.bounds.min + 1).with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.max.y)
                            .with_z(alt + height),
                    })),
                    Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x + 1,
                            self.bounds.min.y - storey_increase + 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height),
                    })),
                    _ => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - storey_increase + 1,
                            self.bounds.min.y + 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height),
                    })),
                }
            } else {
                match self.front {
                    Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                        min: (self.bounds.min + 1).with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x, self.bounds.max.y + storey_increase)
                            .with_z(alt + height),
                    })),
                    Dir::X => painter.prim(Primitive::Aabb(Aabb {
                        min: (self.bounds.min + 1).with_z(alt + previous_height),
                        max: (Vec2::new(self.bounds.max.x + storey_increase, self.bounds.max.y))
                            .with_z(alt + height),
                    })),
                    Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x + 1,
                            self.bounds.min.y - storey_increase + 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height),
                    })),
                    _ => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - storey_increase + 1,
                            self.bounds.min.y + 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height),
                    })),
                }
            };
            let outer_level = match self.front {
                Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                    min: self.bounds.min.with_z(alt + previous_height),
                    max: (Vec2::new(
                        self.bounds.max.x + 1,
                        self.bounds.max.y + storey_increase + 1,
                    ))
                    .with_z(alt + height),
                })),
                Dir::X => painter.prim(Primitive::Aabb(Aabb {
                    min: self.bounds.min.with_z(alt + previous_height),
                    max: Vec2::new(
                        self.bounds.max.x + storey_increase + 1,
                        self.bounds.max.y + 1,
                    )
                    .with_z(alt + height),
                })),
                Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x, self.bounds.min.y - storey_increase)
                        .with_z(alt + previous_height),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + height),
                })),
                _ => painter.prim(Primitive::Aabb(Aabb {
                    min: Vec2::new(self.bounds.min.x - storey_increase, self.bounds.min.y)
                        .with_z(alt + previous_height),
                    max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                        .with_z(alt + height),
                })),
            };

            let is_lower = i <= 1;
            let style = if is_lower {
                self.lower_style
            } else {
                self.upper_style
            };
            let wall_block_fill = if is_lower {
                wall_fill_lower.clone()
            } else {
                wall_fill_upper.clone()
            };
            painter.fill(outer_level, wall_block_fill);
            painter.fill(inner_level, Fill::Block(Block::empty()));

            let walls = outer_level
                .union(inner_level)
                .without(outer_level.intersect(inner_level));

            // Wall Pillars
            // Only upper non-stone floors have wooden beams in the walls
            if !matches!(style, Style::Stone) {
                let mut pillars_y = painter.prim(Primitive::Empty);
                let mut overhang_supports = painter.prim(Primitive::Empty);

                for x in self.tile_aabr.min.x - 2..self.tile_aabr.max.x + 2 {
                    if self.overhang >= 2 && self.front.is_y() {
                        let temp = match self.front {
                            Dir::Y => site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y)),
                            //2 => site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y)),
                            _ => Vec2::zero(),
                        };
                        // NOTE: Orientation 2 doesn't work for some reason. I believe it is
                        // something to do with AABBs with min and max not smaller in the right
                        // order. The same thing is true for orientation 3.
                        let support = match self.front {
                            Dir::Y => painter.line(
                                Vec2::new(
                                    temp.x,
                                    self.bounds.max.y + storey_increase - self.overhang + 1,
                                )
                                .with_z(alt + previous_height - 3),
                                Vec2::new(
                                    temp.x,
                                    self.bounds.max.y + storey_increase - self.overhang + 2,
                                )
                                .with_z(alt + previous_height),
                                0.75,
                            ),
                            //2 => {
                            //    painter.line(
                            //    Vec2::new(temp.x, self.bounds.min.y - storey_increase -
                            // 6).with_z(alt + previous_height + 30),
                            //    Vec2::new(temp.x + 1, self.bounds.min.y - storey_increase
                            // - 3).with_z(alt + previous_height - 3), 1.0)
                            //},
                            _ => painter.prim(Primitive::Empty),
                        };
                        if temp.x <= self.bounds.max.x && temp.x >= self.bounds.min.x {
                            overhang_supports =
                                painter.prim(Primitive::union(overhang_supports, support));
                        }
                    }
                    let pillar = painter.prim(Primitive::Aabb(Aabb {
                        min: site
                            .tile_wpos(Vec2::new(x, self.tile_aabr.min.y - 4))
                            .with_z(alt + previous_height),
                        max: (site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y + 4))
                            + Vec2::unit_x())
                        .with_z(alt + height),
                    }));
                    pillars_y = painter.prim(Primitive::union(pillars_y, pillar));
                }
                let mut pillars_x = painter.prim(Primitive::Empty);
                for y in self.tile_aabr.min.y - 2..self.tile_aabr.max.y + 2 {
                    if self.overhang >= 2 && !self.front.is_y() {
                        let temp = match self.front {
                            Dir::Y => Vec2::zero(),
                            Dir::X => site.tile_wpos(Vec2::new(self.tile_aabr.max.x, y)),
                            Dir::NegY => Vec2::zero(),
                            _ => site.tile_wpos(Vec2::new(self.tile_aabr.min.x, y)),
                        };
                        let support = match self.front {
                            Dir::Y => painter.prim(Primitive::Empty),
                            Dir::X => painter.line(
                                Vec2::new(
                                    self.bounds.max.x + storey_increase - self.overhang + 1,
                                    temp.y,
                                )
                                .with_z(alt + previous_height - 3),
                                Vec2::new(
                                    self.bounds.max.x + storey_increase - self.overhang + 2,
                                    temp.y,
                                )
                                .with_z(alt + previous_height),
                                0.75,
                            ),
                            Dir::NegY => painter.prim(Primitive::Empty),
                            _ => painter.line(
                                Vec2::new(
                                    self.bounds.min.x - storey_increase + self.overhang - 1,
                                    temp.y,
                                )
                                .with_z(alt + previous_height - 3),
                                Vec2::new(
                                    self.bounds.min.x - storey_increase + self.overhang - 2,
                                    temp.y,
                                )
                                .with_z(alt + previous_height),
                                0.75,
                            ),
                        };
                        if temp.y <= self.bounds.max.y && temp.y >= self.bounds.min.y {
                            overhang_supports =
                                painter.prim(Primitive::union(overhang_supports, support));
                        }
                    }
                    let pillar = painter.prim(Primitive::Aabb(Aabb {
                        min: site
                            .tile_wpos(Vec2::new(self.tile_aabr.min.x - 4, y))
                            .with_z(alt + previous_height),
                        max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 4, y))
                            + Vec2::unit_y())
                        .with_z(alt + height),
                    }));
                    pillars_x = painter.prim(Primitive::union(pillars_x, pillar));
                }
                let front_wall = if self.overhang < -4 && i > 1 {
                    painter.prim(Primitive::Empty)
                } else {
                    match self.front {
                        Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - 1,
                                self.bounds.max.y + storey_increase,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + 1,
                                self.bounds.max.y + storey_increase + 1,
                            )
                            .with_z(alt + height),
                        })),
                        Dir::X => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.max.x + storey_increase,
                                self.bounds.min.y - 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + storey_increase + 1,
                                self.bounds.max.y + 1,
                            )
                            .with_z(alt + height),
                        })),
                        Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - 1,
                                self.bounds.min.y - storey_increase,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + 1,
                                self.bounds.min.y - storey_increase + 1,
                            )
                            .with_z(alt + height),
                        })),
                        _ => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - storey_increase,
                                self.bounds.min.y - 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.min.x - storey_increase + 1,
                                self.bounds.max.y + 1,
                            )
                            .with_z(alt + height),
                        })),
                    }
                };
                let pillars1 = if self.front.is_y() {
                    painter.prim(Primitive::intersect(pillars_y, front_wall))
                } else {
                    painter.prim(Primitive::intersect(pillars_x, front_wall))
                };
                let pillars2 = painter.prim(Primitive::intersect(pillars_x, pillars_y));
                let pillars3 = painter.prim(Primitive::union(pillars1, pillars2));
                let pillars4 = match self.front {
                    Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.min.x - 1, self.bounds.min.y - 1)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + 1,
                            self.bounds.max.y + storey_increase + 1,
                        )
                        .with_z(alt + previous_height + 1),
                    })),
                    Dir::X => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.min.x - 1, self.bounds.min.y - 1)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + storey_increase + 1,
                            self.bounds.max.y + 1,
                        )
                        .with_z(alt + previous_height + 1),
                    })),
                    Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - 1,
                            self.bounds.min.y - storey_increase - 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                            .with_z(alt + previous_height + 1),
                    })),
                    _ => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - storey_increase - 1,
                            self.bounds.min.y - 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1)
                            .with_z(alt + previous_height + 1),
                    })),
                };
                let pillars = if matches!(style, Style::Brick) {
                    pillars4
                } else {
                    painter.prim(Primitive::union(pillars3, pillars4))
                };
                painter.fill(
                    painter.prim(Primitive::intersect(walls, pillars)),
                    pillar_fill.clone(),
                );

                painter.fill(overhang_supports, pillar_fill.clone());
            }

            // Windows x axis
            {
                let mut windows = painter.prim(Primitive::Empty);
                for y in self.tile_aabr.min.y - 2..self.tile_aabr.max.y + 2 {
                    let min = (site.tile_wpos(Vec2::new(self.tile_aabr.min.x - 4, y))
                        + Vec2::unit_y() * 2)
                        .with_z(alt + previous_height + 2);
                    let max = (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 4, y + 1))
                        + Vec2::new(1, -1))
                    .with_z(alt + previous_height + 2 + window_height);
                    let window = painter.prim(Primitive::Aabb(Aabb { min, max }));
                    let add_windows = match self.front {
                        Dir::Y => {
                            max.y < self.bounds.max.y + storey_increase && min.y > self.bounds.min.y
                        },
                        Dir::X => max.y < self.bounds.max.y && min.y > self.bounds.min.y,
                        Dir::NegY => {
                            max.y < self.bounds.max.y && min.y > self.bounds.min.y - storey_increase
                        },
                        _ => max.y < self.bounds.max.y && min.y > self.bounds.min.y,
                    };
                    if add_windows {
                        windows = painter.prim(Primitive::union(windows, window));
                    }
                }
                painter.fill(
                    painter.prim(Primitive::intersect(walls, windows)),
                    Fill::Block(Block::air(SpriteKind::Window1).with_ori(2).unwrap()),
                );
                // Wall lamps
                if i == 1 {
                    let mut torches_min = painter.prim(Primitive::Empty);
                    let mut torches_max = painter.prim(Primitive::Empty);
                    for y in self.tile_aabr.min.y..self.tile_aabr.max.y {
                        let pos = site
                            .tile_wpos(Vec2::new(self.tile_aabr.min.x, y))
                            .with_z(alt + previous_height + 3)
                            + Vec3::new(-1, 0, 0);
                        let torch = painter.prim(Primitive::Aabb(Aabb {
                            min: pos,
                            max: pos + 1,
                        }));
                        torches_min = painter.prim(Primitive::union(torches_min, torch));

                        let pos = site
                            .tile_wpos(Vec2::new(self.tile_aabr.max.x, y + 1))
                            .with_z(alt + previous_height + 3)
                            + Vec3::new(1, 0, 0);
                        let torch = painter.prim(Primitive::Aabb(Aabb {
                            min: pos,
                            max: pos + 1,
                        }));
                        torches_max = painter.prim(Primitive::union(torches_max, torch));
                    }
                    painter.fill(
                        torches_min,
                        Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(6).unwrap()),
                    );
                    painter.fill(
                        torches_max,
                        Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(2).unwrap()),
                    );
                }
            }
            // Windows y axis
            {
                let mut windows = painter.prim(Primitive::Empty);
                for x in self.tile_aabr.min.x - 2..self.tile_aabr.max.x + 2 {
                    let min = (site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y - 4))
                        + Vec2::unit_x() * 2)
                        .with_z(alt + previous_height + 2);
                    let max = (site.tile_wpos(Vec2::new(x + 1, self.tile_aabr.max.y + 4))
                        + Vec2::new(-1, 1))
                    .with_z(alt + previous_height + 2 + window_height);
                    let window = painter.prim(Primitive::Aabb(Aabb { min, max }));
                    let add_windows = match self.front {
                        Dir::Y => max.x < self.bounds.max.x && min.x > self.bounds.min.x,
                        Dir::X => {
                            max.x < self.bounds.max.x + storey_increase && min.x > self.bounds.min.x
                        },
                        Dir::NegY => max.x < self.bounds.max.x && min.x > self.bounds.min.x,
                        _ => {
                            max.x < self.bounds.max.x && min.x > self.bounds.min.x - storey_increase
                        },
                    };
                    if add_windows {
                        windows = painter.prim(Primitive::union(windows, window));
                    };
                }
                painter.fill(
                    painter.prim(Primitive::intersect(walls, windows)),
                    Fill::Block(Block::air(SpriteKind::Window1).with_ori(0).unwrap()),
                );
                // Wall lamps
                if i == 1 {
                    let mut torches_min = painter.prim(Primitive::Empty);
                    let mut torches_max = painter.prim(Primitive::Empty);
                    for x in self.tile_aabr.min.x..self.tile_aabr.max.x {
                        let pos = site
                            .tile_wpos(Vec2::new(x + 1, self.tile_aabr.min.y))
                            .with_z(alt + previous_height + 3)
                            + Vec3::new(0, -1, 0);
                        let torch = painter.prim(Primitive::Aabb(Aabb {
                            min: pos,
                            max: pos + 1,
                        }));
                        torches_min = painter.prim(Primitive::union(torches_min, torch));

                        let pos = site
                            .tile_wpos(Vec2::new(x, self.tile_aabr.max.y))
                            .with_z(alt + previous_height + 3)
                            + Vec3::new(0, 1, 0);
                        let torch = painter.prim(Primitive::Aabb(Aabb {
                            min: pos,
                            max: pos + 1,
                        }));
                        torches_max = painter.prim(Primitive::union(torches_max, torch));
                    }
                    painter.fill(
                        torches_min,
                        Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(0).unwrap()),
                    );
                    painter.fill(
                        torches_max,
                        Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap()),
                    );
                }
            }

            // Shed roof on negative overhangs
            if self.overhang < -4 && i > 1 {
                let shed = match self.front {
                    Dir::Y => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - 1,
                                self.bounds.max.y + storey_increase + 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + 1,
                                self.bounds.max.y + storey_increase + self.overhang.abs() + 1,
                            )
                            .with_z(alt + height),
                        },
                        inset: storey,
                        dir: Dir::NegY,
                    }),
                    Dir::X => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.max.x + storey_increase + 1,
                                self.bounds.min.y - 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + storey_increase + self.overhang.abs() + 1,
                                self.bounds.max.y + 1,
                            )
                            .with_z(alt + height),
                        },
                        inset: storey,
                        dir: Dir::NegX,
                    }),
                    Dir::NegY => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - 1,
                                self.bounds.min.y - storey_increase - self.overhang.abs(),
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + 2,
                                self.bounds.min.y - storey_increase,
                            )
                            .with_z(alt + height),
                        },
                        inset: storey,
                        dir: Dir::Y,
                    }),
                    _ => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - storey_increase - self.overhang.abs(),
                                self.bounds.min.y - 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.min.x - storey_increase + 1,
                                self.bounds.max.y + 2,
                            )
                            .with_z(alt + height),
                        },
                        inset: storey,
                        dir: Dir::X,
                    }),
                };
                let shed_empty = match self.front {
                    Dir::Y => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - 1,
                                self.bounds.max.y + storey_increase + 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + 1,
                                self.bounds.max.y + storey_increase + self.overhang.abs(),
                            )
                            .with_z(alt + height - 1),
                        },
                        inset: storey - 1,
                        dir: Dir::NegY,
                    }),
                    Dir::X => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.max.x + storey_increase + 1,
                                self.bounds.min.y - 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + storey_increase + self.overhang.abs(),
                                self.bounds.max.y + 1,
                            )
                            .with_z(alt + height - 1),
                        },
                        inset: storey - 1,
                        dir: Dir::NegX,
                    }),
                    Dir::NegY => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - 1,
                                self.bounds.min.y - storey_increase - self.overhang.abs() + 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + 2,
                                self.bounds.min.y - storey_increase + 1,
                            )
                            .with_z(alt + height),
                        },
                        inset: storey - 1,
                        dir: Dir::Y,
                    }),
                    _ => painter.prim(Primitive::Ramp {
                        aabb: Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - storey_increase - self.overhang.abs() + 1,
                                self.bounds.min.y - 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.min.x - storey_increase + 1,
                                self.bounds.max.y + 2,
                            )
                            .with_z(alt + height),
                        },
                        inset: storey - 1,
                        dir: Dir::X,
                    }),
                };
                painter.fill(shed, Fill::Brick(BlockKind::Wood, self.roof_color, 24));
                painter.fill(shed_empty, Fill::Block(Block::empty()));
                let shed_left_wall = match self.front {
                    Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.min.x, self.bounds.max.y + storey_increase + 1)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.min.x + 1,
                            self.bounds.max.y + storey_increase + self.overhang.abs(),
                        )
                        .with_z(alt + height - 1),
                    })),
                    Dir::X => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.min.y)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + storey_increase + self.overhang.abs(),
                            self.bounds.min.y + 1,
                        )
                        .with_z(alt + height - 1),
                    })),
                    Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.max.x,
                            self.bounds.min.y - storey_increase - self.overhang.abs() + 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + 1,
                            self.bounds.min.y - storey_increase + 1,
                        )
                        .with_z(alt + height),
                    })),
                    _ => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - storey_increase - self.overhang.abs() + 1,
                            self.bounds.max.y,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.min.x - storey_increase + 1,
                            self.bounds.max.y + 1,
                        )
                        .with_z(alt + height),
                    })),
                };
                let shed_right_wall = match self.front {
                    Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.max.x, self.bounds.max.y + storey_increase + 1)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + 1,
                            self.bounds.max.y + storey_increase + self.overhang.abs(),
                        )
                        .with_z(alt + height - 1),
                    })),
                    Dir::X => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.max.y)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + storey_increase + self.overhang.abs(),
                            self.bounds.max.y + 1,
                        )
                        .with_z(alt + height - 1),
                    })),
                    Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x,
                            self.bounds.min.y - storey_increase - self.overhang.abs() + 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.min.x + 1,
                            self.bounds.min.y - storey_increase + 1,
                        )
                        .with_z(alt + height),
                    })),
                    _ => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - storey_increase - self.overhang.abs() + 1,
                            self.bounds.min.y,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.min.x - storey_increase + 1,
                            self.bounds.min.y + 1,
                        )
                        .with_z(alt + height),
                    })),
                };
                let shed_wall_beams = match self.front {
                    Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.min.x, self.bounds.max.y + storey_increase + 1)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + 1,
                            self.bounds.max.y + storey_increase + self.overhang.abs(),
                        )
                        .with_z(alt + previous_height + 1),
                    })),
                    Dir::X => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.min.y)
                            .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + storey_increase + self.overhang.abs(),
                            self.bounds.max.y + 1,
                        )
                        .with_z(alt + previous_height + 1),
                    })),
                    Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x,
                            self.bounds.min.y - storey_increase - self.overhang.abs() + 1,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.max.x + 1,
                            self.bounds.min.y - storey_increase + 1,
                        )
                        .with_z(alt + previous_height + 1),
                    })),
                    _ => painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(
                            self.bounds.min.x - storey_increase - self.overhang.abs() + 1,
                            self.bounds.min.y,
                        )
                        .with_z(alt + previous_height),
                        max: Vec2::new(
                            self.bounds.min.x - storey_increase + 1,
                            self.bounds.max.y + 1,
                        )
                        .with_z(alt + previous_height + 1),
                    })),
                };
                let shed_walls = painter.prim(Primitive::union(shed_left_wall, shed_right_wall));
                painter.fill(
                    painter.prim(Primitive::intersect(shed_walls, shed_empty)),
                    wall_fill_upper.clone(),
                );
                painter.fill(
                    painter.prim(Primitive::intersect(shed_wall_beams, shed_walls)),
                    pillar_fill.clone(),
                );

                // Dormers
                let range = if self.front.is_y() {
                    self.tile_aabr.min.x - 3..self.tile_aabr.max.x + 3
                } else {
                    self.tile_aabr.min.y - 3..self.tile_aabr.max.y + 3
                };
                for n in range {
                    let temp = match self.front {
                        Dir::Y => site.tile_wpos(Vec2::new(n, self.tile_aabr.max.y)) - 4,
                        Dir::X => site.tile_wpos(Vec2::new(self.tile_aabr.max.x, n)) - 4,
                        Dir::NegY => site.tile_wpos(Vec2::new(n, self.tile_aabr.min.y)) - 4,
                        _ => site.tile_wpos(Vec2::new(self.tile_aabr.min.x, n)) - 4,
                    };
                    let dormer_box = match self.front {
                        Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(temp.x - 1, self.bounds.max.y + storey_increase + 1)
                                .with_z(alt + previous_height),
                            max: Vec2::new(
                                temp.x + 4,
                                self.bounds.max.y + storey_increase + self.overhang.abs(),
                            )
                            .with_z(alt + height - 1),
                        })),
                        Dir::X => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(self.bounds.max.x + storey_increase + 1, temp.y - 1)
                                .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + storey_increase + self.overhang.abs(),
                                temp.y + 4,
                            )
                            .with_z(alt + height - 1),
                        })),
                        Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                temp.x - 1,
                                self.bounds.min.y - storey_increase - self.overhang.abs() + 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(temp.x + 4, self.bounds.min.y - storey_increase - 1)
                                .with_z(alt + height - 1),
                        })),
                        _ => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - storey_increase - self.overhang.abs() + 1,
                                temp.y - 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(self.bounds.min.x - storey_increase - 1, temp.y + 4)
                                .with_z(alt + height - 1),
                        })),
                    };
                    let dormer_roof = match self.front {
                        Dir::Y => painter.prim(Primitive::Gable {
                            aabb: Aabb {
                                min: Vec2::new(temp.x - 1, self.bounds.max.y + storey_increase + 1)
                                    .with_z(alt + height - 2),
                                max: Vec2::new(
                                    temp.x + 4,
                                    self.bounds.max.y + storey_increase + self.overhang.abs(),
                                )
                                .with_z(alt + height + 1),
                            },
                            inset: 3,
                            dir: Dir::Y,
                        }),
                        Dir::X => painter.prim(Primitive::Gable {
                            aabb: Aabb {
                                min: Vec2::new(self.bounds.max.x + storey_increase + 1, temp.y - 1)
                                    .with_z(alt + height - 2),
                                max: Vec2::new(
                                    self.bounds.max.x + storey_increase + self.overhang.abs(),
                                    temp.y + 4,
                                )
                                .with_z(alt + height + 1),
                            },
                            inset: 3,
                            dir: Dir::X,
                        }),
                        Dir::NegY => painter.prim(Primitive::Gable {
                            aabb: Aabb {
                                min: Vec2::new(
                                    temp.x - 1,
                                    self.bounds.min.y - storey_increase - self.overhang.abs() + 1,
                                )
                                .with_z(alt + height - 2),
                                max: Vec2::new(temp.x + 4, self.bounds.min.y - storey_increase)
                                    .with_z(alt + height + 1),
                            },
                            inset: 3,
                            dir: Dir::Y,
                        }),
                        _ => painter.prim(Primitive::Gable {
                            aabb: Aabb {
                                min: Vec2::new(
                                    self.bounds.min.x - storey_increase - self.overhang.abs() + 1,
                                    temp.y - 1,
                                )
                                .with_z(alt + height - 2),
                                max: Vec2::new(self.bounds.min.x - storey_increase, temp.y + 4)
                                    .with_z(alt + height + 1),
                            },
                            inset: 3,
                            dir: Dir::X,
                        }),
                    };
                    let window_min = match self.front {
                        Dir::Y => Vec2::new(
                            temp.x,
                            self.bounds.max.y + storey_increase + self.overhang.abs() - 1,
                        )
                        .with_z(alt + previous_height + 2),
                        Dir::X => Vec2::new(
                            self.bounds.max.x + storey_increase + self.overhang.abs() - 1,
                            temp.y,
                        )
                        .with_z(alt + previous_height + 2),
                        Dir::NegY => Vec2::new(
                            temp.x,
                            self.bounds.min.y - storey_increase - self.overhang.abs() + 1,
                        )
                        .with_z(alt + previous_height + 2),
                        _ => Vec2::new(
                            self.bounds.min.x - storey_increase - self.overhang.abs() + 1,
                            temp.y,
                        )
                        .with_z(alt + previous_height + 2),
                    };
                    let window_max = match self.front {
                        Dir::Y => Vec2::new(
                            temp.x + 3,
                            self.bounds.max.y + storey_increase + self.overhang.abs(),
                        )
                        .with_z(alt + previous_height + 2 + window_height),
                        Dir::X => Vec2::new(
                            self.bounds.max.x + storey_increase + self.overhang.abs(),
                            temp.y + 3,
                        )
                        .with_z(alt + previous_height + 2 + window_height),
                        Dir::NegY => Vec2::new(
                            temp.x + 3,
                            self.bounds.min.y - storey_increase - self.overhang.abs() + 2,
                        )
                        .with_z(alt + previous_height + 2 + window_height),
                        _ => Vec2::new(
                            self.bounds.min.x - storey_increase - self.overhang.abs() + 2,
                            temp.y + 3,
                        )
                        .with_z(alt + previous_height + 2 + window_height),
                    };
                    let window = painter.prim(Primitive::Aabb(Aabb {
                        min: window_min,
                        max: window_max,
                    }));
                    let window_cavity = match self.front {
                        Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(temp.x, self.bounds.max.y + storey_increase)
                                .with_z(alt + previous_height),
                            max: Vec2::new(
                                temp.x + 3,
                                self.bounds.max.y + storey_increase + self.overhang.abs() - 1,
                            )
                            .with_z(alt + previous_height + 2 + window_height),
                        })),
                        Dir::X => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(self.bounds.max.x + storey_increase, temp.y)
                                .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + storey_increase + self.overhang.abs() - 1,
                                temp.y + 3,
                            )
                            .with_z(alt + previous_height + 2 + window_height),
                        })),
                        Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                temp.x,
                                self.bounds.min.y - storey_increase - self.overhang.abs() + 2,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(temp.x + 3, self.bounds.min.y - storey_increase + 1)
                                .with_z(alt + previous_height + 2 + window_height),
                        })),
                        _ => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x - storey_increase - self.overhang.abs() + 2,
                                temp.y,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(self.bounds.min.x - storey_increase + 1, temp.y + 3)
                                .with_z(alt + previous_height + 2 + window_height),
                        })),
                    };
                    let valid_dormer = if self.front.is_y() {
                        window_min.x > self.bounds.min.x && window_max.x < self.bounds.max.x
                    } else {
                        window_min.y > self.bounds.min.y && window_max.y < self.bounds.max.y
                    };
                    let window_ori = if self.front.is_y() { 0 } else { 2 };
                    if valid_dormer {
                        painter.fill(
                            painter.prim(Primitive::without(dormer_box, shed)),
                            wall_fill_upper.clone(),
                        );
                        painter.fill(
                            painter.prim(Primitive::without(dormer_roof, shed)),
                            Fill::Brick(BlockKind::Wood, self.roof_color, 24),
                        );
                        painter.fill(window_cavity, Fill::Block(Block::empty()));
                        painter.fill(
                            window,
                            Fill::Block(
                                Block::air(SpriteKind::Window1)
                                    .with_ori(window_ori)
                                    .unwrap(),
                            ),
                        );
                    }
                }
            }

            // Floor
            // No extra floor needed for the ground floor
            if i > 1 {
                let floor = if self.overhang < -1 && i > 1 {
                    match self.front {
                        Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                            min: (self.bounds.min + 1).with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x,
                                self.bounds.max.y + storey_increase + self.overhang.abs(),
                            )
                            .with_z(alt + previous_height + 1),
                        })),
                        Dir::X => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1)
                                .with_z(alt + previous_height),
                            max: Vec2::new(
                                self.bounds.max.x + storey_increase + self.overhang.abs(),
                                self.bounds.max.y,
                            )
                            .with_z(alt + previous_height + 1),
                        })),
                        Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x + 1,
                                self.bounds.min.y + 1 - storey_increase - self.overhang.abs(),
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(self.bounds.max.x, self.bounds.max.y)
                                .with_z(alt + previous_height + 1),
                        })),
                        _ => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x + 1 - storey_increase - self.overhang.abs(),
                                self.bounds.min.y + 1,
                            )
                            .with_z(alt + previous_height),
                            max: Vec2::new(self.bounds.max.x, self.bounds.max.y)
                                .with_z(alt + previous_height + 1),
                        })),
                    }
                } else {
                    match self.front {
                        Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                            min: (self.bounds.min + 1).with_z(alt + previous_height),
                            max: (Vec2::new(
                                self.bounds.max.x,
                                self.bounds.max.y + storey_increase,
                            ))
                            .with_z(alt + previous_height + 1),
                        })),
                        Dir::X => painter.prim(Primitive::Aabb(Aabb {
                            min: (self.bounds.min + 1).with_z(alt + previous_height),
                            max: (Vec2::new(
                                self.bounds.max.x + storey_increase,
                                self.bounds.max.y,
                            ))
                            .with_z(alt + previous_height + 1),
                        })),
                        Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x + 1,
                                self.bounds.min.y + 1 - storey_increase,
                            )
                            .with_z(alt + previous_height),
                            max: (Vec2::new(self.bounds.max.x, self.bounds.max.y))
                                .with_z(alt + previous_height + 1),
                        })),
                        _ => painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(
                                self.bounds.min.x + 1 - storey_increase,
                                self.bounds.min.y + 1,
                            )
                            .with_z(alt + previous_height),
                            max: (Vec2::new(self.bounds.max.x, self.bounds.max.y))
                                .with_z(alt + previous_height + 1),
                        })),
                    }
                };
                painter.fill(
                    floor,
                    Fill::Block(Block::new(BlockKind::Rock, Rgb::new(89, 44, 14))),
                );
            }

            // interior furniture sprites
            let base = alt + (storey * (i as i32 - 1)).max(0);
            if i % 2 == 0 {
                // bedroom on even-leveled floors
                let bed_pos = match self.front {
                    Dir::X => Vec2::new(half_x, quarter_y),
                    Dir::NegY => Vec2::new(three_quarter_x, half_y),
                    _ => Vec2::new(half_x, half_y),
                };
                let bed_dir = self.front;
                let bed_aabr = painter.bed_wood_woodland(bed_pos.with_z(base), bed_dir);
                let nightstand_pos = bed_dir
                    .opposite()
                    .select_aabr_with(bed_aabr, bed_dir.rotated_ccw().select_aabr(bed_aabr))
                    .with_z(base)
                    + bed_dir.rotated_ccw().to_vec2();
                // drawer next to bed
                painter.sprite(nightstand_pos.with_z(base), SpriteKind::DrawerWoodWoodlandS);
                // collectible on top of drawer
                let rng0 = RandomField::new(0).get(nightstand_pos.with_z(base + 1));
                let rng1 = RandomField::new(1).get(nightstand_pos.with_z(base + 1));
                painter.owned_resource_sprite(
                    nightstand_pos.with_z(base + 1),
                    match rng0 % 5 {
                        0 => SpriteKind::Lantern,
                        1 => SpriteKind::PotionMinor,
                        2 => SpriteKind::VialEmpty,
                        3 => SpriteKind::Bowl,
                        _ => SpriteKind::Empty,
                    },
                    (rng1 % 4) as u8 * 2,
                );
                // wardrobe along wall in corner of the room
                let wardrobe_dir = self.front.rotated_cw();
                let bounds = Aabr {
                    min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1),
                    max: Vec2::new(self.bounds.max.x - 2, self.bounds.max.y - 1),
                };
                let wardrobe_pos = wardrobe_dir
                    .opposite()
                    .select_aabr_with(
                        bounds,
                        bounds.center()
                            + wardrobe_dir.vec2(bounds.half_size().w, bounds.half_size().h) / 2,
                    )
                    .with_z(base);

                let sprite = if RandomField::new(0).chance(wardrobe_pos, 0.5) {
                    SpriteKind::WardrobedoubleWoodWoodland
                } else {
                    SpriteKind::WardrobedoubleWoodWoodland2
                };
                painter.mirrored2(wardrobe_pos, wardrobe_dir, sprite);
            } else {
                // living room with table + chairs + random
                for dir in DIRS {
                    // random accent pieces and loot
                    let sprite_pos = self.bounds.center() + dir * 5;
                    let rng0 = RandomField::new(0).get(sprite_pos.with_z(base));
                    let rng1 = RandomField::new(1).get(sprite_pos.with_z(base));
                    painter.owned_resource_sprite(
                        sprite_pos.with_z(base),
                        match rng0 % 32 {
                            0..=2 => SpriteKind::Crate,
                            3..=4 => SpriteKind::CoatrackWoodWoodland,
                            5..=7 => SpriteKind::FlowerpotWoodWoodlandS,
                            8..=9 => SpriteKind::Lantern,
                            _ => SpriteKind::Empty,
                        },
                        (rng1 % 4) as u8 * 2,
                    );
                }

                if self.bounds.max.x - self.bounds.min.x < 16
                    || self.bounds.max.y - self.bounds.min.y < 16
                {
                    let table_pos = Vec2::new(half_x, half_y);
                    // room is smaller, so use small table
                    painter.sprite(
                        table_pos.with_z(base),
                        SpriteKind::DiningtableWoodWoodlandRound,
                    );
                    for dir in Dir::iter() {
                        let chair_pos = table_pos + dir.to_vec2();
                        painter.rotated_sprite(
                            chair_pos.with_z(base),
                            SpriteKind::ChairWoodWoodland,
                            dir.opposite().sprite_ori(),
                        );
                    }
                } else {
                    // room is bigger, so use large table + chair positions
                    let table_pos = match self.front {
                        Dir::Y => Vec2::new(half_x, three_quarter_y),
                        Dir::X => Vec2::new(half_x, half_y),
                        _ => Vec2::new(quarter_x, half_y),
                    }
                    .with_z(base);
                    let table_axis = if RandomField::new(0).chance(table_pos, 0.5) {
                        Dir::X
                    } else {
                        Dir::Y
                    };
                    let table_bounds = painter.table_wood_fancy_woodland(table_pos, table_axis);
                    painter.chairs_around(SpriteKind::ChairWoodWoodland, 1, table_bounds, base);
                }
                // drawer along a wall
                let (drawer_pos, drawer_ori) = match self.front {
                    Dir::Y => (Vec2::new(self.bounds.max.x - 1, self.bounds.max.y - 2), 6),
                    Dir::X => (Vec2::new(self.bounds.max.x - 2, self.bounds.max.y - 1), 0),
                    Dir::NegY => (Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 2), 6),
                    _ => (Vec2::new(self.bounds.min.x + 2, self.bounds.max.y - 1), 0),
                };
                painter.rotated_sprite(
                    drawer_pos.with_z(base),
                    SpriteKind::DrawerWoodWoodlandL1,
                    drawer_ori,
                );
            }

            // Stairs
            if i > 1 {
                let stair_width = 3;
                let previous_floor_height = (storey * (i as i32 - 2)).max(0);
                let stair_origin = match self.front {
                    Dir::Y => self.bounds.min + 1,
                    Dir::X => self.bounds.min + 1,
                    Dir::NegY => {
                        Vec2::new(self.bounds.max.x - 12, self.bounds.max.y - stair_width * 2)
                    },
                    _ => Vec2::new(self.bounds.max.x - 12, self.bounds.min.y + 1),
                };
                let staircase = if i < 2 {
                    painter.prim(Primitive::Empty)
                } else if i % 2 == 0 {
                    let ramp = /*match self.front */{
                        //0 => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x + 3, stair_origin.y).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + 10, stair_origin.y + stair_width).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: Dir::X,
                            })
                        /*},
                        1 => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x, stair_origin.y + 3).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 10).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: 0,
                            })
                        },
                        2 => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x + 3, stair_origin.y).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + 10, stair_origin.y + stair_width).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: 0,
                            })
                        },
                        _ => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x, stair_origin.y + 3).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 10).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: 0,
                            })
                        }*/
                    };
                    let support = {
                        //match self.front {
                        //0 => {
                        painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(stair_origin.x + 10, stair_origin.y)
                                .with_z(alt + previous_floor_height),
                            max: Vec2::new(stair_origin.x + 12, stair_origin.y + stair_width)
                                .with_z(alt + previous_height + 1),
                        }))
                        //},
                        //1 => {
                        //    painter.prim(Primitive::Aabb(Aabb {
                        //        min: Vec2::new(stair_origin.x, stair_origin.y
                        // + 10).with_z(alt + previous_floor_height), max:
                        //   Vec2::new(stair_origin.x + stair_width,
                        // stair_origin.y + 12).with_z(alt + previous_height +
                        // 1),    }))
                        //},
                        //2 => {
                        //    painter.prim(Primitive::Aabb(Aabb {
                        //        min: Vec2::new(stair_origin.x + 10,
                        // stair_origin.y).with_z(alt + previous_floor_height),
                        //        max: Vec2::new(stair_origin.x + 12,
                        // stair_origin.y + stair_width).with_z(alt +
                        // previous_height + 1),    }))
                        //},
                        //_ => {
                        //    painter.prim(Primitive::Aabb(Aabb {
                        //        min: Vec2::new(stair_origin.x, stair_origin.y
                        // + 10).with_z(alt + previous_floor_height), max:
                        //   Vec2::new(stair_origin.x + stair_width,
                        // stair_origin.y + 12).with_z(alt + previous_height +
                        // 1),    }))
                        //},
                    };
                    painter.prim(Primitive::union(ramp, support))
                } else {
                    let ramp = /*match self.front */{
                        //0 => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x + 1, stair_origin.y + stair_width).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + 8, stair_origin.y + 2 * stair_width).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: Dir::NegX,
                            })
                        /*},
                        1 => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 1).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + 2 * stair_width, stair_origin.y + 8).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: 1,
                            })
                        },
                        2 => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x + 1, stair_origin.y + stair_width).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + 8, stair_origin.y + 2 * stair_width).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: 1,
                            })
                        },
                        _ => {
                            painter.prim(Primitive::Ramp {
                                aabb: Aabb {
                                    min: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 1).with_z(alt + previous_floor_height),
                                    max: Vec2::new(stair_origin.x + 2 * stair_width, stair_origin.y + 8).with_z(alt + previous_height + 1),
                                },
                                inset: storey,
                                dir: 1,
                            })
                        },
                        */
                    };
                    let support = {
                        //match self.front {
                        //0 => {
                        painter.prim(Primitive::Aabb(Aabb {
                            min: Vec2::new(stair_origin.x, stair_origin.y + stair_width)
                                .with_z(alt + previous_floor_height),
                            max: Vec2::new(stair_origin.x + 2, stair_origin.y + 2 * stair_width)
                                .with_z(alt + previous_height + 1),
                        }))
                        //},
                        //1 => {
                        //    painter.prim(Primitive::Aabb(Aabb {
                        //        min: Vec2::new(stair_origin.x + stair_width,
                        // stair_origin.y).with_z(alt + previous_floor_height),
                        //        max: Vec2::new(stair_origin.x + 2 *
                        // stair_width, stair_origin.y + 2).with_z(alt +
                        // previous_height + 1),    }))
                        //},
                        //2 => {
                        //    painter.prim(Primitive::Aabb(Aabb {
                        //        min: Vec2::new(stair_origin.x, stair_origin.y
                        // + stair_width).with_z(alt + previous_floor_height),
                        //   max: Vec2::new(stair_origin.x + 2,
                        // stair_origin.y + 2 * stair_width).with_z(alt +
                        // previous_height + 1),    }))
                        //},
                        //_ => {
                        //    painter.prim(Primitive::Aabb(Aabb {
                        //        min: Vec2::new(stair_origin.x + stair_width,
                        // stair_origin.y).with_z(alt + previous_floor_height),
                        //        max: Vec2::new(stair_origin.x + 2 *
                        // stair_width, stair_origin.y + 2).with_z(alt +
                        // previous_height + 1),    }))
                        //},
                    };
                    painter.prim(Primitive::union(ramp, support))
                };
                let stairwell = if i < 2 {
                    painter.prim(Primitive::Empty)
                } else if i % 2 == 0 {
                    painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(stair_origin.x + 2, stair_origin.y)
                            .with_z(alt + previous_floor_height + 1),
                        max: Vec2::new(stair_origin.x + 9, stair_origin.y + stair_width)
                            .with_z(alt + previous_height + 1),
                    }))
                    //match self.front {
                    //    0 => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x,
                    // stair_origin.y).with_z(alt + previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x + 9,
                    // stair_origin.y + stair_width).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //    1 => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x,
                    // stair_origin.y).with_z(alt + previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x +
                    // stair_width, stair_origin.y + 9).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //    2 => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x,
                    // stair_origin.y).with_z(alt + previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x + 9,
                    // stair_origin.y + stair_width).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //    _ => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x,
                    // stair_origin.y).with_z(alt + previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x +
                    // stair_width, stair_origin.y + 9).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //}
                } else {
                    painter.prim(Primitive::Aabb(Aabb {
                        min: Vec2::new(stair_origin.x + 2, stair_origin.y + stair_width)
                            .with_z(alt + previous_floor_height + 1),
                        max: Vec2::new(stair_origin.x + 11, stair_origin.y + 2 * stair_width)
                            .with_z(alt + previous_height + 1),
                    }))
                    //match self.front {
                    //    0 => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x + 2,
                    // stair_origin.y + stair_width).with_z(alt +
                    // previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x + 11,
                    // stair_origin.y + 2 * stair_width).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //    1 => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x +
                    // stair_width, stair_origin.y + 2).with_z(alt +
                    // previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x + 2 *
                    // stair_width, stair_origin.y + 11).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //    2 => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x + 2,
                    // stair_origin.y + stair_width).with_z(alt +
                    // previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x + 11,
                    // stair_origin.y + 2 * stair_width).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //    _ => {
                    //        painter.prim(Primitive::Aabb(Aabb {
                    //                min: Vec2::new(stair_origin.x +
                    // stair_width, stair_origin.y + 2).with_z(alt +
                    // previous_floor_height + 1),
                    //                max: Vec2::new(stair_origin.x + 2 *
                    // stair_width, stair_origin.y + 11).with_z(alt +
                    // previous_height + 1),        }))
                    //    },
                    //}
                };

                painter.fill(stairwell, Fill::Block(Block::empty()));
                painter.fill(
                    staircase,
                    Fill::Block(Block::new(BlockKind::Rock, Rgb::new(89, 44, 14))),
                );
            }
        }

        // Foundation
        painter.fill(
            painter.prim(Primitive::Aabb(Aabb {
                min: (self.bounds.min - 1).with_z(self.alt - foundations),
                max: (self.bounds.max + 2).with_z(self.alt + 1),
            })),
            Fill::Block(Block::new(BlockKind::Rock, Rgb::new(31, 33, 32))),
        );

        // Fireplace and chimney
        let fireplace_origin = /*if self.levels > 1 {
            match self.front {
                0 => {
                    Vec2::new(self.bounds.min.x + 12, self.bounds.min.y + 1)
                },
                1 => {
                    Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 12)
                },
                2 => {
                    Vec2::new(self.bounds.max.x - 12, self.bounds.max.y - 1)
                },
                _ => {
                    Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 12)
                },
            }
        } else */{
            match self.front {
                Dir::Y => {
                    Vec2::new(half_x, self.bounds.min.y + 1)
                },
                Dir::X => {
                    Vec2::new(self.bounds.min.x + 1, half_y)
                },
                Dir::NegY => {
                    Vec2::new(half_x - 4, self.bounds.max.y - 3)
                },
                _ => {
                    Vec2::new(self.bounds.max.x - 3, half_y)
                },
            }
        };
        let chimney = match self.front {
            Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 4, fireplace_origin.y + 3)
                    .with_z(alt + roof + roof_height + 2),
            })),
            Dir::X => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 4)
                    .with_z(alt + roof + roof_height + 2),
            })),
            Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 4, fireplace_origin.y + 3)
                    .with_z(alt + roof + roof_height + 2),
            })),
            _ => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 4)
                    .with_z(alt + roof + roof_height + 2),
            })),
        };

        let chimney_cavity = match self.front {
            Dir::Y => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2)
                    .with_z(alt + roof + roof_height + 2),
            })),
            Dir::X => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3)
                    .with_z(alt + roof + roof_height + 2),
            })),
            Dir::NegY => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2)
                    .with_z(alt + roof + roof_height + 2),
            })),
            _ => painter.prim(Primitive::Aabb(Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3)
                    .with_z(alt + roof + roof_height + 2),
            })),
        };
        let fire_embers = match self.front {
            Dir::Y => Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2).with_z(alt + 1),
            },
            Dir::X => Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3).with_z(alt + 1),
            },
            Dir::NegY => Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2).with_z(alt + 1),
            },
            _ => Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3).with_z(alt + 1),
            },
        };
        let fireplace_cavity = match self.front {
            Dir::Y => Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 3).with_z(alt + 2),
            },
            Dir::X => Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 3).with_z(alt + 2),
            },
            Dir::NegY => Aabb {
                min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2).with_z(alt + 2),
            },
            _ => Aabb {
                min: Vec2::new(fireplace_origin.x, fireplace_origin.y + 1).with_z(alt),
                max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3).with_z(alt + 2),
            },
        };

        painter.fill(
            chimney,
            Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24),
        );
        painter.fill(chimney_cavity, Fill::Block(Block::empty()));
        painter.fill(
            painter.prim(Primitive::Aabb(fireplace_cavity)),
            Fill::Block(Block::empty()),
        );
        painter.fill(
            painter.prim(Primitive::Aabb(fire_embers)),
            Fill::Block(Block::air(SpriteKind::Ember)),
        );

        // bedroom on first level corners in case there is no 2nd level
        if self.levels == 1 {
            // different positions for smaller houses
            let bed_pos = if self.bounds.max.x - self.bounds.min.x < 16
                || self.bounds.max.y - self.bounds.min.y < 16
            {
                match self.front {
                    Dir::Y => Vec2::new(self.bounds.min.x + 4, self.bounds.min.y + 2),
                    Dir::X => Vec2::new(self.bounds.min.x + 2, self.bounds.min.y + 4),
                    Dir::NegY => Vec2::new(self.bounds.max.x - 4, self.bounds.max.y - 2),
                    _ => Vec2::new(self.bounds.max.x - 8, self.bounds.max.y - 2),
                }
            } else {
                match self.front {
                    Dir::Y => Vec2::new(self.bounds.max.x - 4, self.bounds.min.y + 2),
                    Dir::X => Vec2::new(self.bounds.min.x + 8, self.bounds.max.y - 2),
                    Dir::NegY => Vec2::new(self.bounds.max.x - 4, self.bounds.max.y - 2),
                    _ => Vec2::new(self.bounds.max.x - 8, self.bounds.max.y - 2),
                }
            };
            let bed_dir = self.front.abs();

            painter.bed_wood_woodland(bed_pos.with_z(alt), bed_dir);
        }

        if self.christmas_decorations {
            let (wreath_pos, wreath_ori) = match self.front {
                Dir::Y => (
                    Aabb {
                        min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 3)
                            .with_z(alt + 2),
                        max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 4)
                            .with_z(alt + 3),
                    },
                    4,
                ),
                Dir::X => (
                    Aabb {
                        min: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 1)
                            .with_z(alt + 2),
                        max: Vec2::new(fireplace_origin.x + 4, fireplace_origin.y + 2)
                            .with_z(alt + 3),
                    },
                    2,
                ),
                Dir::NegY => (
                    Aabb {
                        min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y - 1)
                            .with_z(alt + 2),
                        max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y).with_z(alt + 3),
                    },
                    0,
                ),
                _ => (
                    Aabb {
                        min: Vec2::new(fireplace_origin.x - 1, fireplace_origin.y + 1)
                            .with_z(alt + 2),
                        max: Vec2::new(fireplace_origin.x, fireplace_origin.y + 2).with_z(alt + 3),
                    },
                    6,
                ),
            };
            painter.fill(
                painter.prim(Primitive::Aabb(wreath_pos)),
                Fill::Block(
                    Block::air(SpriteKind::ChristmasWreath)
                        .with_ori(wreath_ori)
                        .unwrap(),
                ),
            );
        }

        // Door
        // Fill around the door with wall
        let doorway1 = match self.front {
            Dir::Y => Aabb {
                min: Vec2::new(door_tile_wpos.x - 1, self.bounds.max.y).with_z(alt),
                max: Vec2::new(door_tile_wpos.x + 3, self.bounds.max.y + 1).with_z(alt + 4),
            },
            Dir::X => Aabb {
                min: Vec2::new(self.bounds.max.x, door_tile_wpos.y - 1).with_z(alt),
                max: Vec2::new(self.bounds.max.x + 1, door_tile_wpos.y + 3).with_z(alt + 4),
            },
            Dir::NegY => Aabb {
                min: Vec2::new(door_tile_wpos.x - 1, self.bounds.min.y).with_z(alt),
                max: Vec2::new(door_tile_wpos.x + 3, self.bounds.min.y + 1).with_z(alt + 4),
            },
            _ => Aabb {
                min: Vec2::new(self.bounds.min.x, door_tile_wpos.y - 1).with_z(alt),
                max: Vec2::new(self.bounds.min.x + 1, door_tile_wpos.y + 3).with_z(alt + 4),
            },
        };
        painter.fill(painter.prim(Primitive::Aabb(doorway1)), wall_fill_lower);

        // Carve out the doorway with air
        let doorway2 = match self.front {
            Dir::Y => Aabb {
                min: Vec2::new(door_tile_wpos.x, self.bounds.max.y).with_z(alt),
                max: Vec2::new(door_tile_wpos.x + 2, self.bounds.max.y + 1).with_z(alt + 3),
            },
            Dir::X => Aabb {
                min: Vec2::new(self.bounds.max.x, door_tile_wpos.y).with_z(alt),
                max: Vec2::new(self.bounds.max.x + 1, door_tile_wpos.y + 2).with_z(alt + 3),
            },
            Dir::NegY => Aabb {
                min: Vec2::new(door_tile_wpos.x, self.bounds.min.y).with_z(alt),
                max: Vec2::new(door_tile_wpos.x + 2, self.bounds.min.y + 1).with_z(alt + 3),
            },
            _ => Aabb {
                min: Vec2::new(self.bounds.min.x, door_tile_wpos.y).with_z(alt),
                max: Vec2::new(self.bounds.min.x + 1, door_tile_wpos.y + 2).with_z(alt + 3),
            },
        };
        painter.fill(
            painter.prim(Primitive::Aabb(doorway2)),
            Fill::Block(Block::empty()),
        );

        // Fill in the right and left side doors
        let (door_gap, door1, door1_ori, door2, door2_ori) = match self.front {
            Dir::Y => (
                Aabb {
                    min: Vec2::new(door_tile_wpos.x - 1, self.bounds.max.y + 1).with_z(alt),
                    max: Vec2::new(door_tile_wpos.x + 3, self.bounds.max.y + 4).with_z(alt + 3),
                },
                Aabb {
                    min: Vec2::new(door_tile_wpos.x, self.bounds.max.y).with_z(alt),
                    max: Vec2::new(door_tile_wpos.x + 1, self.bounds.max.y + 1).with_z(alt + 1),
                },
                0,
                Aabb {
                    min: Vec2::new(door_tile_wpos.x + 1, self.bounds.max.y).with_z(alt),
                    max: Vec2::new(door_tile_wpos.x + 2, self.bounds.max.y + 1).with_z(alt + 1),
                },
                4,
            ),
            Dir::X => (
                Aabb {
                    min: Vec2::new(self.bounds.max.x + 1, door_tile_wpos.y - 1).with_z(alt),
                    max: Vec2::new(self.bounds.max.x + 4, door_tile_wpos.y + 3).with_z(alt + 3),
                },
                Aabb {
                    min: Vec2::new(self.bounds.max.x, door_tile_wpos.y).with_z(alt),
                    max: Vec2::new(self.bounds.max.x + 1, door_tile_wpos.y + 1).with_z(alt + 1),
                },
                2,
                Aabb {
                    min: Vec2::new(self.bounds.max.x, door_tile_wpos.y + 1).with_z(alt),
                    max: Vec2::new(self.bounds.max.x + 1, door_tile_wpos.y + 2).with_z(alt + 1),
                },
                6,
            ),
            Dir::NegY => (
                Aabb {
                    min: Vec2::new(door_tile_wpos.x - 1, self.bounds.min.y - 4).with_z(alt),
                    max: Vec2::new(door_tile_wpos.x + 3, self.bounds.min.y).with_z(alt + 3),
                },
                Aabb {
                    min: Vec2::new(door_tile_wpos.x, self.bounds.min.y).with_z(alt),
                    max: Vec2::new(door_tile_wpos.x + 1, self.bounds.min.y + 1).with_z(alt + 1),
                },
                0,
                Aabb {
                    min: Vec2::new(door_tile_wpos.x + 1, self.bounds.min.y).with_z(alt),
                    max: Vec2::new(door_tile_wpos.x + 2, self.bounds.min.y + 1).with_z(alt + 1),
                },
                4,
            ),
            _ => (
                Aabb {
                    min: Vec2::new(self.bounds.min.x - 4, door_tile_wpos.y - 1).with_z(alt),
                    max: Vec2::new(self.bounds.min.x, door_tile_wpos.y + 3).with_z(alt + 3),
                },
                Aabb {
                    min: Vec2::new(self.bounds.min.x, door_tile_wpos.y).with_z(alt),
                    max: Vec2::new(self.bounds.min.x + 1, door_tile_wpos.y + 1).with_z(alt + 1),
                },
                2,
                Aabb {
                    min: Vec2::new(self.bounds.min.x, door_tile_wpos.y + 1).with_z(alt),
                    max: Vec2::new(self.bounds.min.x + 1, door_tile_wpos.y + 2).with_z(alt + 1),
                },
                6,
            ),
        };
        painter.fill(
            painter.prim(Primitive::Aabb(door_gap)),
            Fill::Block(Block::air(SpriteKind::Empty)),
        );
        painter.fill(
            painter.prim(Primitive::Aabb(door1)),
            Fill::Block(Block::air(SpriteKind::Door).with_ori(door1_ori).unwrap()),
        );
        painter.fill(
            painter.prim(Primitive::Aabb(door2)),
            Fill::Block(Block::air(SpriteKind::Door).with_ori(door2_ori).unwrap()),
        );
        if self.christmas_decorations {
            // we need to randomize position to see both variants
            let rng = RandomField::new(0).get(door_tile_wpos.with_z(alt + 3));
            let right = (rng % 2) as i32;
            let (door_light_pos, door_light_ori) = match self.front {
                Dir::Y => (
                    Aabb {
                        min: Vec2::new(door_tile_wpos.x + right, self.bounds.max.y + 1)
                            .with_z(alt + 3),
                        max: Vec2::new(door_tile_wpos.x + 1 + right, self.bounds.max.y + 2)
                            .with_z(alt + 4),
                    },
                    4,
                ),
                Dir::X => (
                    Aabb {
                        min: Vec2::new(self.bounds.max.x + 1, door_tile_wpos.y + right)
                            .with_z(alt + 3),
                        max: Vec2::new(self.bounds.max.x + 2, door_tile_wpos.y + 1 + right)
                            .with_z(alt + 4),
                    },
                    2,
                ),
                Dir::NegY => (
                    Aabb {
                        min: Vec2::new(door_tile_wpos.x + right, self.bounds.min.y - 1)
                            .with_z(alt + 3),
                        max: Vec2::new(door_tile_wpos.x + 1 + right, self.bounds.min.y)
                            .with_z(alt + 4),
                    },
                    0,
                ),
                _ => (
                    Aabb {
                        min: Vec2::new(self.bounds.min.x - 1, door_tile_wpos.y + right)
                            .with_z(alt + 3),
                        max: Vec2::new(self.bounds.min.x, door_tile_wpos.y + 1 + right)
                            .with_z(alt + 4),
                    },
                    6,
                ),
            };
            painter.fill(
                painter.prim(Primitive::Aabb(door_light_pos)),
                Fill::Block(
                    Block::air(SpriteKind::ChristmasOrnament)
                        .with_ori(door_light_ori)
                        .unwrap(),
                ),
            );
        }
    }
}
